/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef __COID_SERVER_PLUGINSCANNER__HEADER_FILE__
#define __COID_SERVER_PLUGINSCANNER__HEADER_FILE__

#include "coid/comm/hash/hashkeyset.h"
#include "coid/comm/dir.h"

#include "serverface.h"
#include "service.h"
#include "treemgr.h"


namespace coid {

extern version  GserverVersion;

////////////////////////////////////////////////////////////////////////////////
///Plugin&service scanner
struct PluginScannerCoid : CoidNode
{
    uint _scit;
    dynarray<charstr> _wds;
    dynarray<charstr> _nodevlist;
    int _port;

    static void* thread_update_plugin_list(void* p);
    void spawn_update_plugin_list()
    {
        spawn_thread( (t_thread_fnc)thread_update_plugin_list, this, true, "update_plugin_list" );
    }

    opcd init();
    opcd load_cfg( bool firstrun );

    int get_port() const            { return _port; }

    PluginScannerCoid( int port )
    {
//        _wd = path;
        _port = port;
        _scit = 0;
        _cfg_loaded = false;
        _fload_all = true;
        object_name("plugin scanner");
		_mutex.set_name( "PluginScannerCoid" );
    }

    virtual token short_name() const        { static token _T("devscan"); return _T; }

    virtual ~PluginScannerCoid()
    {
    }

    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_BEGIN_PURE(PluginScannerCoid,"0.0",CoidNode);
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;

protected:
    opcd update_plugin_list();
    opcd scan_directory( directory& dir, AcceptSvcCbk& AS, dynarray< NODEREF<ServiceCoid> >& newsvc );
    opcd load_device( directory& dirdev, AcceptSvcCbk& AS, dynarray< NODEREF<ServiceCoid> >& newsvc );
    bool check_device_load( token fname );
    opcd dependency_sort( dynarray< NODEREF<ServiceCoid> >& svc );
    
    static uint find_svc_in_list( const dynarray< NODEREF<ServiceCoid> >& lst, const token& name )
    {
        for( uints i=0; i<lst.size(); ++i )
        {
            if( name == lst[i]->_desc._name )
                return (uint)i;
        }
        return UMAX32;
    }

    struct DEP_SRT
    {
        bool operator() (const uint a, const uint b)
        {
            if (b == _idx)
            {
                if (_bits[a/32] & (1<<(a%32)))  //a should be before
                    return true;
            }
            return false;
        }

        explicit DEP_SRT (const uint* bits, uint idx) : _bits(bits), _idx(idx)
        {
        }

        const uint* _bits;
        uint _idx;
    };


    struct service_params
    {
        charstr _name;
        //version _ver;

        bool    _load;
        ushort  _port;                  ///< acceptor port override
        charstr _wd;                    ///< working directory override

        operator token() const          { return _name; }
    };


    typedef hash_keyset<service_params,_Select_Copy<service_params,token> >    t_hash;

    t_hash  _hash;
    bool    _fload_all;
    bool    _cfg_loaded;


    struct AcceptSvc : AcceptSvcCbk
    {
        virtual opcd accept( ServiceDescriptor& d, charstr& wd ) const
        {
            return _psc.accept_service( d, wd );
        }

        PluginScannerCoid& _psc;

        AcceptSvc( PluginScannerCoid& p ) : _psc(p) {}
    };

public:
    opcd accept_service( ServiceDescriptor& d, charstr& wd ) const
    {
        if( d._coid_version._majorminor != GserverVersion._majorminor )
            return ersINVALID_VERSION "invalid COID version";

        const service_params* psp = _hash.find_value( d._name );
        if(!psp)
        {
            if( _fload_all  &&  d._acceptor_port != 0 )
                return 0;
            else if( d._acceptor_port == UMAX32 )
                return ersREJECTED "default non loading";
            else
                return ersINVALID_PARAMS "unspecified acceptor port for service";
        }

        if( !psp->_load )
            return ersREJECTED "load config rejects the service";

        if( psp->_port != 0  &&  d._acceptor_port != UMAX32 )
            d._acceptor_port = psp->_port;

        if( d._acceptor_port == 0 )     //configuration was required to specify port
            return ersINVALID_PARAMS "unspecified acceptor port for service";

        if( !psp->_wd.is_empty() )
        {
            directory::append_path( wd, psp->_wd );
            directory::treat_trailing_separator( wd, false );

            if( !directory::is_valid_directory(wd) )
                post_warning(0) << "specified working directory '" << wd << "' of service '" << d._name << "' does not exist" << EOM;
        }
        return 0;
    }

};


} //namespace coid

#endif //__COID_SERVER_PLUGINSCANNER__HEADER_FILE__
